#ifndef FLUIDPARTICLE_H
#define FLUIDPARTICLE_H

#include "Particle.h"

/// @file FluidParticle.h
/// @brief the basic unit of the lagrangian simulation
/// @author Perseedoss Rajiv
/// @version 1.0
/// @date 22.07.11
/// Revision History :
/// Initial Version 22.07.11
/// @class FluidParticle
/// @brief stores all attributes for a fluid for use with SPH

class FluidParticle : public Particle
{
public:
    /// @brief ctor
    FluidParticle();

    /// @brief ctor
    FluidParticle(const FluidParticle& _fluidParticle);

    /// @brief ctor
    /// @param[in] _id the id
    /// @param[in] _mass the mass
    /// @param[in] _density the density
    /// @param[in] _position the position
    /// @param[in] _velocity the velocity
    /// @param[in] _viscosityConstant the viscosity constant
    /// @param[in] _gasConstant the gas constant
    /// @param[in] _surfaceTensionCoefficient the surface tension coefficient
    /// @param[in] _surfaceTensionThreshold the surface tension threshold
    /// @param[in] _surfaceColorCoefficient the surface tension color
    /// @param[in] _interfaceTensionCoefficient the interface tension coefficient
    /// @param[in] _interfaceTensionThreshold the interface tension threshold
    /// @param[in] _interfaceColorCoefficient the interface tension color
    /// @param[in] _colour the visual colour of the particle
    /// @param[in] _radius the radius
    /// @param[in] _name the name of the fluid to which this particle belongs
    /// @param[in] _waitUntilFirstHitBoundary flag to determine whether the particle hits boundary first before behaving as a fluid
    /// @param[in] _waitUntilFirstHitRBD flag to determine whether the particle hits rbd first before behaving as a fluid
    FluidParticle
        (
            const int _id,
            const ngl::Real _mass = 0,
            const ngl::Real _density = 0,
            const ngl::Vector _position = 0,
            const ngl::Vector _velocity = 0,
            const ngl::Real _viscosityConstant = 0,
            const ngl::Real _gasConstant = 0,
            const ngl::Real _surfaceTensionCoefficient = 0,
            const ngl::Real _surfaceTensionThreshold = 0,
            const ngl::Real _surfaceColorCoefficient = 0,
            const ngl::Real _interfaceTensionCoefficient = 0,
            const ngl::Real _interfaceTensionThreshold = 0,
            const ngl::Real _interfaceColorCoefficient = 0,
            const ngl::Colour _colour = 0,
            const ngl::Real _radius = 0,
            const std::string _name = "na",
            const bool _waitUntilFirstHitBoundary = false,
            const bool _waitUntilFirstHitRBD = false
        );

    /// @brief dtor
    ~FluidParticle();

    /// @brief calculate the scalar pressure
    void calculatePressure();

    /// @brief reset the accumulated force
    void resetForce();

    /// @brief get the name of the fluid to which this particle belongs to
    inline std::string getName() const { return m_name; }

    /// @brief get the pressure force
    inline ngl::Vector getPressureForce() const { return m_pressureForce; }

    /// @brief set the pressure force
    /// @param[in] _f force value
    inline void setPressureForce(const ngl::Vector _f) { m_pressureForce = _f; }

    /// @brief get the viscosity force
    inline ngl::Vector getViscosityForce() const { return m_viscosityForce; }

    /// @brief set the viscosity force
    /// @param[in] _f force value
    inline void setViscosityForce(const ngl::Vector _f) { m_viscosityForce = _f; }

    /// @brief get the surface tension force
    inline ngl::Vector getSurfaceTensionForce() const { return m_surfaceTensionForce; }

    /// @brief set the surface tension force
    /// @param[in] _f force value
    inline void setSurfaceTensionForce(const ngl::Vector _f) { m_surfaceTensionForce = _f; }

    /// @brief get the interface tension force
    inline ngl::Vector getInterfaceTensionForce() const { return m_interfaceTensionForce; }

    /// @brief set the interface tension force
    /// @param[in] _f force value
    inline void setInterfaceTensionForce(const ngl::Vector _f) { m_interfaceTensionForce = _f; }

    /// @brief get the gravitational force
    inline ngl::Vector getGravityForce() const { return m_gravityForce; }

    /// @brief set the gravitational force
    /// @param[in] _f force value
    inline void setGravityForce(const ngl::Vector _f) { m_gravityForce = _f; }

    /// @brief get the pressure
    inline ngl::Real getPressure() const { return m_pressure; }

    /// @brief get the density
    inline ngl::Real getDensity() const { return m_density; }

    /// @brief set the density
    /// @param[in] _d density value
    inline void setDensity(const ngl::Real _d) { m_density = _d; }

    /// @brief get the rest density
    inline ngl::Real getMaterialRestDensity() const { return m_materialRestDensity; }

    /// @brief set the rest density
    /// @param[in] _v density value
    inline void setMaterialRestDensity(const ngl::Real _v) { m_materialRestDensity = _v; }

    /// @brief get the gas constant
    inline ngl::Real getGasConstant() const { return m_gasConstant; }

    /// @brief set the gas constant force
    /// @param[in] _c value
    inline void setGasConstant(const ngl::Real _c) { m_gasConstant = _c; }

    /// @brief get the viscosity constant
    inline ngl::Real getViscosityConstant() const { return m_viscosityConstant; }

    /// @brief set the viscosity constant
    /// @param[in] _c value
    inline void setViscosityConstant(const ngl::Real _c) { m_viscosityConstant = _c; }

    /// @brief get the surface tension coefficient
    inline ngl::Real getSurfaceTensionCoefficient() const { return m_surfaceTensionCoefficient; }

    /// @brief set the surface interface coefficient
    /// @param[in] _c coefficient value
    inline void setSurfaceTensionCoefficient(const ngl::Real _c) { m_surfaceTensionCoefficient = _c; }

    /// @brief get the surface tension threshold
    inline ngl::Real getSurfaceTensionThreshold() const { return m_surfaceTensionThreshold; }

    /// @brief set the surface tension threshold
    /// @param[in] _c threshold value
    inline void setSurfaceTensionThreshold(const ngl::Real _c) { m_surfaceTensionThreshold = _c; }

    /// @brief get the surface tension color
    inline ngl::Real getSurfaceColorCoefficient() const { return m_surfaceColorCoefficient; }

    /// @brief set the surface color
    /// @param[in] _c color value
    inline void setSurfaceColorCoefficient(const ngl::Real _c) { m_surfaceColorCoefficient = _c; }

    /// @brief get the interface tension coefficient
    inline ngl::Real getInterfaceTensionCoefficient() const { return m_interfaceTensionCoefficient; }

    /// @brief set the surface tension coefficient
    /// @param[in] _c coefficient value
    inline void setInterfaceTensionCoefficient(const ngl::Real _c) { m_interfaceTensionCoefficient = _c; }

    /// @brief get the interface tension threshold
    inline ngl::Real getInterfaceTensionThreshold() const { return m_interfaceTensionThreshold; }

    /// @brief set the interface tension threshold
    /// @param[in] _c threshold value
    inline void setInterfaceTensionThreshold(const ngl::Real _c) { m_interfaceTensionThreshold = _c; }

    /// @brief get the interface tension color
    inline ngl::Real getInterfaceColorCoefficient() const { return m_interfaceColorCoefficient; }

    /// @brief set the interface color
    /// @param[in] _c color value
    inline void setInterfaceColorCoefficient(const ngl::Real _c) { m_interfaceColorCoefficient = _c; }

    /// @brief update the properties of the particle to check when it can start behaving as a fluid
    /// @param[in] _hitBoundary flag to determine if object hit boundary
    /// @param[in] _hitRBD flag to determine if object hit rbd
    void updateHitForTheFirstTime
                (
                    const bool _hitBoundary,
                    const bool _hitRBD
                );

    /// @brief check if particle can act as a fluid
    inline bool fluidRulesEnabled() const { return (m_waitUntilFirstHitBoundary || m_waitUntilFirstHitRBD) ? m_hasHitEnvironmentYet : true; }

    /// @brief set flag to determine if object can behave as a fluid when first hit the boundary
    /// @param[in] _v flag value
    inline void setWaitUntilFirstHitBoundary(const bool _v) { m_waitUntilFirstHitBoundary = _v; }

    /// @brief set flag to determine if object can behave as a fluid when first hit the rbds
    /// @param[in] _v flag value
    inline void setWaitUntilFirstHitRBD(const bool _v) { m_waitUntilFirstHitRBD = _v; }

private:
    /// @brief name of fluid for identification by user later on
    std::string m_name;

    /// @brief pressure force
    ngl::Vector m_pressureForce;

    /// @brief viscosity force
    ngl::Vector m_viscosityForce;

    /// @brief surface tension force
    ngl::Vector m_surfaceTensionForce;

    /// @brief interface tension force
    ngl::Vector m_interfaceTensionForce;

    /// @brief gravity force
    ngl::Vector m_gravityForce;

    /// @brief density at particle
    ngl::Real m_density;

    /// @brief rest density at particle
    ngl::Real m_materialRestDensity;

    /// @brief pressure at particle
    ngl::Real m_pressure;

    /// @brief viscosity constant
    ngl::Real m_viscosityConstant;

    /// @brief gas constant
    ngl::Real m_gasConstant;

    /// @brief surface tension coefficient
    ngl::Real m_surfaceTensionCoefficient;

    /// @brief surface tension threshold
    ngl::Real m_surfaceTensionThreshold;

    /// @brief surface color coefficient
    ngl::Real m_surfaceColorCoefficient;

    /// @brief interface tension coefficient
    ngl::Real m_interfaceTensionCoefficient;

    /// @brief interface tension threshold
    ngl::Real m_interfaceTensionThreshold;

    /// @brief interface color coefficient
    ngl::Real m_interfaceColorCoefficient;

    /// @brief flag to show whether the particle has hit the environment yet
    bool m_hasHitEnvironmentYet;

    /// @brief flag to set whether particle wait until first hit the boundary before behaving as a fluid
    bool m_waitUntilFirstHitBoundary;

    /// @brief flag to set whether particle wait until first hit the rbd before behaving as a fluid
    bool m_waitUntilFirstHitRBD;

};

#endif // FLUIDPARTICLE_H
